/**
 * 
 */
package reservationLogic;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import org.apache.commons.lang3.StringUtils;

import encryption.Encrypt;
import reservationEntity.ReservationEntity;
import reservationService.ReservationService;
import userController.Navigator;
import userEntity.UserEntity;
import validator.Validator;

/**
 * @author Aakash Singh
 *
 */
public class ReservationLogic {

	/**
	 * This method validates the pickup date and time
	 * @param pickUpDate
	 * @param returnDate
	 * @return boolean
	 */
	public boolean isPickupDateBeforeeturnDate(LocalDate pickUpDate, LocalDate returnDate){

		if (!pickUpDate.isBefore(returnDate) && !pickUpDate.equals(returnDate) )
		{
			return true;
		}
	return false;
	}
	
	/**
	 * This method registers the user in the user table and the customer table if not present initially
	 * @param us
	 * @return
	 */
	public boolean registerUser(UserEntity us) throws SQLException
	{
		ReservationService rs = new ReservationService();
		try {
			if(rs.registerUser(us))
			return true;
			else
				return false;
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
	}
	public UserEntity checkMember(String phoneNumber) throws SQLException
	{
		UserEntity us= new UserEntity();
		ReservationService rs = new ReservationService();
		ResultSet res= rs.checkMember(phoneNumber);
		try {
			if(res.next())
			{
			     us.setPhoneNumber(res.getString(1));
			     us.setName(res.getString(2));
			     us.setEmailID(res.getString(6));
			     us.setAddress(res.getString(3));
			     return us;
			}
			rs.closeConnection();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw e;
		}
		return null;
	}
	
	/**
	 * This method checks whether the dates not before the current date
	 * @param date
	 * @return boolean
	 */
	public boolean isDateBeforeCurrentDate(LocalDate date){
		if(!Validator.validateFutureDate(date))
		{
			return true;
		}
		return false;
	}
	
	
	/**
	 * This method fetches a list of specifications available for a given vehicle class
	 * @param vehicleType
	 * @return
	 */
	public List<String> fetchSpecification(String vehicleClass) throws SQLException{
		
		ReservationService rs = new ReservationService();
		ResultSet res= rs.fetchSpecification(vehicleClass);
		ArrayList<String> list= new ArrayList<String>();
		try 
		{
			
			while(res.next())
			{
			list.add(res.getString("spec"));
			}
			rs.closeConnection();
		}
		catch(Exception e)
		{
			e.printStackTrace();
			throw e;
		}
		return list;
	}
	
	/**
	 * This method fetches a list of vehicle type available for a given vehicle specification
	 *  @pre Vehicle Class and Specification inputs are validated
	 * @post An arraylist containing the vehicle type for the particular vehicle class and specification is returned
	 * @param specification
	 * @return
	 */
	public List<String> fetchVehicleType(String specification, String vehicleClass,LocalDate pickupDate,LocalDate returnDate) throws SQLException{
		ReservationService rs = new ReservationService();
		ResultSet res= rs.fetchVehicleType(specification, vehicleClass,pickupDate,returnDate);
		ArrayList<String> list= new ArrayList<String>();
		try 
		{
			
			while(res.next())
			{
			list.add(res.getString("vehicleType"));
			}
			rs.closeConnection();
		}
		catch(Exception e)
		{
			e.printStackTrace();
			throw e;
		}
		return list;
	}
	
	
	public List<String> fetchVehicleTypeRent( String vehicleClass) throws SQLException{
		ReservationService rs = new ReservationService();
		ResultSet res= rs.fetchVehicleTypeRent(vehicleClass);
		ArrayList<String> list= new ArrayList<String>();
		try 
		{
			
			while(res.next())
			{
			list.add(res.getString("vehicleType"));
			}
			rs.closeConnection();
		}
		catch(Exception e)
		{
			e.printStackTrace();
			throw e;
		}
		return list;
	}
	/**
	 * This method fetches a list of add equip available for a given vehicle class
	 * @pre Vehicle Class is validated
	 * @post An arraylist containing the additional equipment for the particular vehicle class is returned
	 * @param vehicleType
	 * @return
	 */
	public ArrayList<String> fetchAddEquip(String vehicleClass) throws SQLException{
		ReservationService rs = new ReservationService();
		ResultSet res= rs.fetchAddEquip(vehicleClass);
		ArrayList<String> list= new ArrayList<String>();
		try 
		{
			
			while(res.next())
			{
			list.add(res.getString("equipmentName"));
			}
			rs.closeConnection();
		}
		catch(Exception e)
		{
			e.printStackTrace();
			throw e;
		}
		return list;
		
	}
	
	/**
	 *  * This method calculates the estimated price quote
	 * @pre All the input made by the user are valid
	 * @post The estimated cost for the choices made by the customer is returned 
	 * @param vehicleType
	 * @param age
	 * @param vehicleClass
	 * @param vehiclePackage
	 * @param addEquip
	 */
	public ReservationEntity calculatePriceQuote(int weeks,int days, int hours,String vehicleType,String age,ArrayList<String> addEquip) throws SQLException{
		// fetchVehicleClassRate()
		// based on the package fetch the rate from the resultset returned
		// fetchAddEquipRate()
		// calculate prices
		
		ReservationService rs = new ReservationService();
		DecimalFormat df = new DecimalFormat("#0.00");
		Double weeklyRentalCost=0.0,hourlyRentalCost=0.0,dailyRentalCost=0.0;
		Double weeklyInsCost=0.0,hourlyInsCost=0.0,dailyInsCost=0.0,dailyFees=0.0,hourlyFees=0.0;
		System.out.println("Vehicle Type :"+vehicleType);
		
		ResultSet res1=rs.fetchAddEquipRate(addEquip);
		
		ResultSet res=rs.fetchVehicleClassRate(vehicleType);
		try
		{
			if(res.next())
			{
				System.out.println("Inside if");
			weeklyRentalCost=res.getDouble(5);
			dailyRentalCost=res.getDouble(4);
			hourlyRentalCost=res.getDouble(3);
			weeklyInsCost=res.getDouble(8);
			dailyInsCost=res.getDouble(7);
			hourlyInsCost=res.getDouble(6);
			
			}
			else
				System.out.println("Record Not found");
			if(res1!=null)
			while(res1.next())
			{
				dailyFees=dailyFees+res1.getDouble(1);
				hourlyFees=hourlyFees+res1.getDouble(2);
				System.out.println("Additional Equipment");
			}
			System.out.println("Daily fees are :" + dailyFees +" Hourly fees of additional equipemnt "+hourlyFees);
			rs.closeConnection();
		}
		catch(Exception ex)
		{
			ex.printStackTrace();
			throw ex;
		}
		
		Double rentalCost=(weeks * weeklyRentalCost) + (days * dailyRentalCost) + (hours * hourlyRentalCost);
		Double insuranceCost=(weeks * weeklyInsCost) + (days * dailyInsCost) + (hours * hourlyInsCost);
		ReservationService rs1= new ReservationService();
		ArrayList<Double> fees= rs1.getFeeConstants();
		double serviceFee=0.0,ageFee=0.0,taxPercentage=0.0,additionalEquipmentFees=0.0;
		if(fees.size()==3)
		{ serviceFee = fees.get(0);
		if(age.equalsIgnoreCase("Between 21 and 24"))
			ageFee = fees.get(1);
			
		  taxPercentage= fees.get(2);
		}
		System.out.println("Service Fees "+serviceFee +" Age Fees "+ageFee + " Tax Percent "+taxPercentage);
		additionalEquipmentFees=((weeks*7)+days)*dailyFees+ (hours*hourlyFees);
		System.out.println("Additional Equipment fees :"+df.format(additionalEquipmentFees));
		Double totalFees = ageFee + serviceFee;
		System.out.println("Total Rental Cost is :"+df.format(rentalCost));
		System.out.println("Total Insurance Cost is :"+df.format(insuranceCost));
		System.out.println("Total Fees Cost is :"+df.format(totalFees));
		double estBeforeTax =rentalCost+insuranceCost+totalFees +additionalEquipmentFees;
		System.out.println("Est Price before taxes :"+df.format(estBeforeTax));
		double taxes = estBeforeTax * ((taxPercentage)/100);
		double estimatedCost = estBeforeTax + taxes;
		System.out.println("Total is :"+df.format(estimatedCost));
		ReservationEntity reserve= new ReservationEntity();
		System.out.println("rental cost" + rentalCost);
		reserve.setRentalCost(rentalCost);
		reserve.setAgeFine(ageFee);
		reserve.setTax(taxes);
		reserve.setInsuranceCost(insuranceCost);
		reserve.setEstimatedCost(estimatedCost);
		reserve.setFees(totalFees);
		reserve.setAdditionalEquipmentFees(additionalEquipmentFees);
		rs.closeConnection();
		return reserve;
	}
	
	/**
	 * This method returns the Fixed fees values which are required during the 
	 * cost calculation for the reservation/return.
	 * @return ArrayList<Double>
	 * 
	 */
	public ArrayList<Double> getFeeConstants() throws SQLException
	{
		ReservationService rs = new ReservationService();
		ArrayList<Double> fees= rs.getFeeConstants();
		rs.closeConnection();
		return fees;
	}
	
	/**
	 * This method stores the reservation details and stores the reseravtionID
	 * @param reservation
	 * @return
	 * @throws Exception 
	 */
	public int storeReservation(ReservationEntity reservation) throws Exception{
		ReservationService rs = new ReservationService();
		try {
		if(reservation.getPhoneNumber() == null || StringUtils.isEmpty(reservation.getPhoneNumber())){
			reservation.setPhoneNumber(Navigator.getPhoneNumber().substring(2, 12));
		}
		ResultSet res=rs.storeReservation(reservation);
		
			if(res.next())
			{
			
				return res.getInt("last_id");
			}
			rs.closeConnection();
		} catch (SQLException e) {
			rs.closeConnection();
			e.printStackTrace();
			throw e;
		} catch (Exception e) {
			rs.closeConnection();
			e.printStackTrace();
			throw e;
		}
		return 0;
	}
	
	/**
	 * this method verifies the reservationId entered contains only integers
	 * @param reservationId
	 * @return
	 */
	public boolean validateReservationID(int reservationId){
		
		return true;
	}
	
	/**
	 * This method fetches the reservation details of the given reservation ID
	 * @pre Reservation ID is validated
	 * @post A reservation Entity is returned with appropriate data member values.
	 * @param reservationId
	 * @return
	 */
	public ReservationEntity fetchReservation(long reservationId) throws SQLException{
		
		ReservationEntity reserve = new ReservationEntity();
		ReservationService rs = new ReservationService();
		ResultSet res=rs.fetchReservation(reservationId);
		ArrayList<String> additionalEquip = new ArrayList<String>();
		
		try {
			
			if(!res.next())
			{
				System.out.println("No next in logic");
				reserve=null;
				return reserve;
			}
			
			do
			{
				System.out.println("there is next in logic "+res.getInt(1));
				reserve.setReservationID(res.getInt(1));
				reserve.setPhoneNumber(res.getString(2));
				reserve.setPickUpDate(LocalDate.parse(res.getDate(4).toString()));
				reserve.setPickUpTime(res.getTime(5).toString());
				reserve.setReturnDate(LocalDate.parse(res.getDate(6).toString()));
				reserve.setReturnTime(res.getTime(7).toString());
				reserve.setVehicleType(res.getString(9));
				reserve.setVehicleClass(res.getString(10));
				reserve.setVehicleSpec(res.getString(11));
				
				reserve.setFees(res.getDouble(13));
				reserve.setRentalCost(res.getDouble(14));
				reserve.setInsuranceCost(res.getDouble(15));
				reserve.setEstimatedCost(res.getDouble(16));
				reserve.setCreditCardNumber(res.getString(17));
				
				reserve.setExpiryDate(res.getInt(18));
				reserve.setStatus(res.getString(19));
				additionalEquip.add(res.getString(21));
				if(res.wasNull())
				{
					additionalEquip=null;
					break;
				}
				
			}while(res.next());	
			
			reserve.setAdditionalEquip(additionalEquip);
			System.out.println("Before return in logic with reserve object "+reserve.getReservationID());
			return reserve;
		
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw e;
		}
		finally{
			rs.closeConnection();
		}
		
	}
	
	/**
	 * This method fetches the reservation details for the given phone number and date
	 * @pre Phone number and date are validated
	 * @post A reservation Entity is returned with appropriate data member values.
	 * @param phoneNumber
	 * @param date
	 * @return
	 */
	public ReservationEntity fetchReservation(String phoneNumber, String date) throws SQLException{
		ReservationEntity reserve = new ReservationEntity();
		ReservationService rs = new ReservationService();
		ResultSet res=rs.fetchReservation(phoneNumber,date);
		
		ArrayList<String> additionalEquip = new ArrayList<String>();
		
		
		try {
			if(!res.next())
			{
				reserve=null;
				System.out.println("No next in logic");
				return reserve;
			}
			
			do
			{	System.out.println("there is next in logic "+res.getInt(1));
				reserve.setReservationID(res.getInt(1));
				reserve.setPhoneNumber(res.getString(2));
				reserve.setPickUpDate(LocalDate.parse(res.getDate(4).toString()));
				reserve.setPickUpTime(res.getTime(5).toString());
				reserve.setReturnDate(LocalDate.parse(res.getDate(6).toString()));
				reserve.setReturnTime(res.getTime(7).toString());
				reserve.setVehicleType(res.getString(9));
				reserve.setVehicleClass(res.getString(10));
				reserve.setVehicleSpec(res.getString(11));
				reserve.setFees(res.getDouble(13));
				reserve.setRentalCost(res.getDouble(14));
				reserve.setInsuranceCost(res.getDouble(15));
				reserve.setEstimatedCost(res.getDouble(16));
				reserve.setCreditCardNumber(""+res.getString(17));
				reserve.setExpiryDate(res.getInt(18));
				reserve.setStatus(res.getString(19));
				additionalEquip.add(res.getString(21));
				if(res.wasNull())
				{
					additionalEquip=null;
					break;
				}
				
			}while(res.next());
			reserve.setAdditionalEquip(additionalEquip);
			System.out.println("Before return in logic with reserve object "+reserve.getReservationID());
			return reserve;
		
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw e;
		}
		finally{
			rs.closeConnection();
		}
		
	}
	
	/**
	 * This method cancels the reservation of the given reservation iD
	 * @param reservationId
	 * @return
	 */
	public String cancelReservation(int reservationId) throws SQLException{
		System.out.println("ID "+reservationId);
		ReservationService rs = new ReservationService();
		rs.cancelReservation(reservationId);
		
		return null;
	}
	
}
